using Gee;

namespace Atlantis
{
	public class Registry : Object
	{
		public HashMap<Type, GLib.Object> singletonObjects;
		public HashMap<Type, Type> knownTypes;
		public HashMap<Type, Type> singletonTypes;
		private HashMap<string, IWorkspace> workspaces;

		public Registry()
		{
			stderr.printf("In Registry.ctr()\n");
			this.singletonTypes = new HashMap<Type, Type>(GLib.direct_hash,GLib.str_equal,GLib.str_equal);
			this.singletonObjects = new HashMap<Type, GLib.Object>(GLib.direct_hash,GLib.str_equal,GLib.str_equal);
			this.knownTypes = new HashMap<Type, Type>(GLib.direct_hash,GLib.str_equal,GLib.str_equal);
			this.workspaces = new HashMap<string, IWorkspace>(GLib.str_hash,GLib.str_equal,GLib.str_equal);
		}
	
		public ResolveSyntax<T> Resolve<T>()
		{
			stderr.printf("In Registry.Resolve<T>()\n");
			var par = this;
			var ret = new ResolveSyntax<T>(par);	
			return ret;
		}

		public T Get<T>()
		{
			T ret = null;
			Type returnType = typeof(T);
			//
			// look in singletons
			//
			if (singletonObjects.contains(returnType))
			{
				return (T)singletonObjects[returnType];
			}	
			
			//
			// look in the singletontypes
			//
			if (singletonTypes.contains(returnType))
			{
				returnType = singletonTypes.get(returnType);
				ret = (T)Object.new(returnType);
				Inject(ret);
				singletonObjects.set(typeof(T), (GLib.Object)ret);
			}		
			
			//
			// look in the knowtypes
			//
			if (knownTypes.contains(returnType))
			{
				returnType = knownTypes.get(returnType);
				ret = (T)Object.new(returnType);
				Inject(ret);
			}
			
			if (ret == null)
			{
				ret = (T)Object.new(returnType);
				Inject(ret);
			}
			
			return ret;
		}

		public void Inject(void* object)
		{
			if (object is IInjectRegistry)
			{
				IInjectRegistry iinjectregistry = (IInjectRegistry)object;
				iinjectregistry.Registry = this;
			}
			
			if (object is IEventHandler)
			{
				IEventHandler ieventHandler = (IEventHandler)object;
				IEventAggregator aggregator = Get<IEventAggregator>();
				aggregator.add_listener(ieventHandler);
			}
		}

		public HashMap<string, IWorkspace> Workspaces
		{
			get
			{
				return this.workspaces;
			}
		}
		
		public class ResolveSyntax<T> : Object
		{
			private Registry registry;

			public ResolveSyntax(Registry registry)
			{
				stderr.printf("In RegistrationSyntax<T>.ctr()\n");
				if (registry == null)
					stderr.printf("registry = null\n");
				this.registry = registry;	// <-- GLib-GObject-CRITICAL **: g_object_ref: assertion `G_IS_OBJECT (object)' failed
				if (registry == null)
					stderr.printf("_registry = null\n");	
			}
			
			public void With<TImplementation>(bool asSingleton = false)
			{
				stderr.printf("In RegistrationSyntax<T>.With<TImplementation>.AsSingleton()\n");
				stderr.printf("typeof(T).name()\n");
				var type = typeof(T);
				stderr.printf("Type implementationType = typeof(TImplementation)\n");
				var implementationType = typeof(TImplementation);
				
				if (asSingleton)
					this.registry.singletonTypes.set(type, implementationType);
				else
					this.registry.knownTypes.set(type, implementationType);
				stderr.printf("Leaving RegistrationSyntax<T>.With<TImplementation>()");
			}
		}
	}
}
